package org.scohen.juploadr.prefs;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;

import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.FormAttachment;
import org.eclipse.swt.layout.FormData;
import org.eclipse.swt.layout.FormLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Dialog;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.TabFolder;
import org.eclipse.swt.widgets.TabItem;
import org.scohen.juploadr.Messages;
import org.scohen.juploadr.ui.MessageUtils;
import org.scohen.juploadr.uploadapi.ImageUploadApi;

/**
 * @author John
 */
public class ConfigurationDialog extends Dialog {

    private static Class[] commonFormTypes = new Class[] { UploadActionForm.class, ConnectionForm.class };
    private static Class[] formTypes;

    private final Collection<ConfigurationForm> configurationForms = new ArrayList<ConfigurationForm>();
    private TabFolder tabs;

    public ConfigurationDialog(Shell parent) {
        super(parent, SWT.APPLICATION_MODAL);
        Class[] apiForms = ImageUploadApi.getInstance().getConfiurationDialogs();
        formTypes = new Class[apiForms.length + commonFormTypes.length];
        System.arraycopy(apiForms, 0, formTypes, 0, apiForms.length);
        System.arraycopy(commonFormTypes, 0, formTypes, apiForms.length, commonFormTypes.length);
    }

    public void open() {
        Shell parent = getParent();
        Shell shell = new Shell(parent, SWT.DIALOG_TRIM | SWT.APPLICATION_MODAL);
        shell.setText(getText());
        shell.setLayout(new FormLayout());

        tabs = createTabs(shell);
        Composite buttonRow = this.createButtonRow(shell);

        // Set up the formData for the tabs.
        FormData formData = new FormData();
        formData.top = new FormAttachment(0, 1);
        formData.left = new FormAttachment(0, 1);
        formData.right = new FormAttachment(100, 1);
        formData.bottom = new FormAttachment(buttonRow);
        tabs.setLayoutData(formData);
        // Set up the formData for the button row.
        formData = new FormData();
        formData.top = new FormAttachment(tabs);
        formData.left = new FormAttachment(0, 1);
        formData.right = new FormAttachment(100, 1);
        formData.bottom = new FormAttachment(100, 1);
        buttonRow.setLayoutData(formData);

        tabs.pack();
        shell.pack();
        shell.open();
        Display display = parent.getDisplay();
        while (!shell.isDisposed()) {
            if (!display.readAndDispatch())
                display.sleep();
        }
        return;
    }

    public void refreshAPIForms() {
        open();
    }

    /**
     * @param shell
     * @return
     */
    private TabFolder createTabs(Shell shell) {
        TabFolder tabs = new TabFolder(shell, SWT.DEFAULT);

        for (int i = 0; i < formTypes.length; i++) {
            Class formType = formTypes[i];
            addConfigFormTab(tabs, formType);
        }
        // If we're going to have plugins, this is the place to dynamically
        // get a list of their ConfigurationForm types and add each.
        return tabs;
    }

    /**
     * @param tabs
     * @param formType
     */
    private void addConfigFormTab(TabFolder tabs, Class formType) {
        try {
            if (!this.validateFormType(formType)) {
                return;
            }
            ConfigurationForm form = newForm(tabs, formType);
            TabItem tab = new TabItem(tabs, SWT.NONE);
            tab.setText(form.getFormName());
            tab.setControl((Control) form);
            ((Control) form).pack();
            this.configurationForms.add(form);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    /**
     * @param tabs
     * @param formType
     * @return
     * @throws NoSuchMethodException
     * @throws InstantiationException
     * @throws IllegalAccessException
     * @throws InvocationTargetException
     */
    private ConfigurationForm newForm(TabFolder tabs, Class formType) throws NoSuchMethodException,
            InstantiationException, IllegalAccessException, InvocationTargetException {
        Constructor constructor = formType.getConstructor(new Class[] { Composite.class });
        ConfigurationForm form = (ConfigurationForm) constructor.newInstance(tabs);
        form.setConfigurationDialog(this);
        return form;
    }

    private boolean validateFormType(Class formType) {
        if (!ConfigurationForm.class.isAssignableFrom(formType)) {
            System.err.println(formType + " is not a ConfigurationForm!"); //$NON-NLS-1$
            return false;
        }
        if (!Control.class.isAssignableFrom(formType)) {
            System.err.println(formType + " is not a Control!"); //$NON-NLS-1$
            return false;
        }
        return true;
    }

    /**
     * @param shell
     */
    private Composite createButtonRow(final Shell shell) {
        Composite buttonRow = new Composite(shell, SWT.NONE);
        GridLayout lay = new GridLayout(2, false);
        buttonRow.setLayout(lay);

        Button ok = new Button(buttonRow, SWT.PUSH);
        ok.setLayoutData(new GridData(SWT.RIGHT, SWT.FILL, true, false));
        ok.setText(Messages.getString("juploadr.ui.button.ok")); //$NON-NLS-1$
        ok.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent e) {
                if (ConfigurationDialog.this.saveChanges()) {
                    shell.close();
                }
            }
		});
        Button cancel = new Button(buttonRow, SWT.PUSH);
        cancel.setLayoutData(new GridData(SWT.RIGHT, SWT.FILL, false, false));
        cancel.setText(Messages.getString("juploadr.ui.button.cancel")); //$NON-NLS-1$
        cancel.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent e) {
				shell.close();
			}
		
		});
        buttonRow.pack();
        shell.setDefaultButton(ok);
        return buttonRow;
    }

    /**
     * Iterates over this.configurationForms and tells each to save its changes.
     */
    protected boolean saveChanges() {
        Collection<Fault> faults = new HashSet<Fault>();
        for (Iterator iter = this.configurationForms.iterator(); iter.hasNext();) {
            ConfigurationForm form = (ConfigurationForm) iter.next();
            Fault fault = form.validateChanges();
            if (fault == null) {
                form.saveChanges();
            } else if (fault.isFatal()) {
                MessageUtils.showErrorMessage(fault.getText());
            } else {
                form.saveChanges();
                MessageUtils.showInfoMessage(fault.getText());
            }
            faults.add(fault);
        }
        return !hasErrors(faults);
    }

    private boolean hasErrors(Collection faults) {
        Iterator iter = faults.iterator();
        while (iter.hasNext()) {
            Fault fault = (Fault) iter.next();
            if (fault!= null && fault.isFatal()) {
                return true;
            }
        }
        return false;
    }

    public void accountChanged() {
        Iterator iter = configurationForms.iterator();
        while (iter.hasNext()) {
            ConfigurationForm form = (ConfigurationForm) iter.next();
            form.accountChanged();
        }
    }

}